use std::fmt::Debug;

use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use serde_xml_rs::from_str;

use super::super::my_cache;
use super::super::my_dbs::Mdb;
use super::super::my_err::MyErr;
use super::super::util_date; 
use super::super::my_dbs::SystemUser;
use super::super::wechat::Wechat;

const COMPONENT_VERIFY_TICKET_KEY: &str = "component_verify_ticket#key"; // 缓存中存放 component_verify_ticket 的 key
const COMPONENT_VERIFY_TOKEN_KEY: &str = "component_access_token#key"; // 缓存中存放 component_access_token 的 key
const PRE_AUTH_CODE_KEY: &str = "pre_auth_code#key"; // 缓存中存放 pre_auth_code 的key
pub const AUTHORIZER_ACCESS_TOKEN: &str = "#AAT#key"; // 授权方令牌，这个令牌跟授权方相关。需要 (授权方APPID + AUTHORIZER_ACCESS_TOKEN) 才能得到正确的key

const GET_COMPONENT_TOKEN_URL: &str = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"; // 获取令牌（component_access_token）的URL地址
const GET_PREAUTHCODE_URL: &str = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token="; // 获取预授权码（pre_auth_code）
const GET_QUERY_AUTH_URL: &str = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="; // 使用授权码获取授权信息(authorizer_access_token等)
const GET_AUTHORIZER_URL: &str = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token="; // 刷新/获取最新的接口调用令牌

///
/// 公众号 authorizer_refresh_token 的需要长久保存在数据库中。
/// 所以使用该功能需要配合数据库进行操作。
/// 表名：weixin_authorizer
/// 建表语句
/// 
/// DROP TABLE IF EXISTS weixin_authorizer;
/// CREATE TABLE weixin_authorizer(
///     id BIGINT NOT NULL AUTO_INCREMENT  COMMENT 'ID' ,
///     authorizer_appid VARCHAR(64) NOT NULL  DEFAULT '' COMMENT '授权方 appid' ,
///     authorizer_access_token VARCHAR(256) NOT NULL  DEFAULT '' COMMENT '接口调用令牌' ,
///     expires_in BIGINT NOT NULL  DEFAULT 0 COMMENT '有效期' ,
///     authorizer_refresh_token VARCHAR(256) NOT NULL  DEFAULT '' COMMENT '刷新令牌' ,
///     func_info VARCHAR(512) NOT NULL  DEFAULT '' COMMENT '权限集列表' ,
///     end_timestamp BIGINT NOT NULL  DEFAULT 0 COMMENT '有效期截止;单位秒' ,
///      VARCHAR(32) NOT NULL  DEFAULT '' COMMENT '昵称' ,
///     head_img VARCHAR(256) NOT NULL  DEFAULT '' COMMENT '头像' ,
///     service_type_info INT NOT NULL  DEFAULT 0 COMMENT '公众号类型' ,
///     verify_type_info INT NOT NULL  DEFAULT 0 COMMENT '公众号认证类型' ,
///     user_name VARCHAR(64) NOT NULL  DEFAULT '' COMMENT '原始 ID' ,
///     principal_name VARCHAR(128) NOT NULL  DEFAULT '' COMMENT '主体名称' ,
///     alias VARCHAR(128) NOT NULL  DEFAULT '' COMMENT '微信号' ,
///     business_info VARCHAR(512) NOT NULL  DEFAULT '' COMMENT '功能的开通状况' ,
///     qrcode_url VARCHAR(256) NOT NULL  DEFAULT '' COMMENT '二维码图片' ,
///     sort INT NOT NULL  DEFAULT 0 COMMENT '排序' ,
///     ext VARCHAR(512) NOT NULL  DEFAULT '' COMMENT '扩展信息' ,
///     status INT NOT NULL  DEFAULT 0 COMMENT '状态' ,
///     PRIMARY KEY (id)
/// )  COMMENT = '微信-授权人'; 
/// 
/// 
/// 开放平台的验证票据需要存储在一个持久数据中，这里使用的是数据库。
/// 表名：sys_settings
/// 建表语句
/// DROP TABLE IF EXISTS sys_settings;
/// CREATE TABLE sys_settings(
///     id BIGINT NOT NULL AUTO_INCREMENT  COMMENT 'id' ,
///     org_id BIGINT NOT NULL  DEFAULT 0 COMMENT '所属机构;全局属性则为 0' ,
///     title VARCHAR(64) NOT NULL  DEFAULT '' COMMENT '标题' ,
///     params_key VARCHAR(64) NOT NULL  DEFAULT '' COMMENT '键' ,
///     params_value VARCHAR(256) NOT NULL  DEFAULT '' COMMENT '值' ,
///     value_type VARCHAR(16) NOT NULL  DEFAULT 'string' COMMENT '值类型' ,
///     ext VARCHAR(256) NOT NULL  DEFAULT '' COMMENT '备注' ,
///     status INT NOT NULL  DEFAULT 0 COMMENT '状态' ,
///     PRIMARY KEY (id)
/// )  COMMENT = '系统_配置参数';
/// 
///  encoding_aes_key

/// 验证票据加密前 xml
#[derive(Debug, Serialize, Deserialize)]
pub struct ComponentVerifyTicketEncXML {
    #[serde(rename = "AppId")]
    app_id: String,
    #[serde(rename = "Encrypt")]
    encrypt: String,
}

/// 验证票据解密后 xml
#[derive(Debug, Serialize, Deserialize)]
pub struct ComponentVerifyTicketDecXML {
    #[serde(rename = "AppId")]
    app_id: String,
    #[serde(rename = "CreateTime")]
    create_time: i64,
    #[serde(rename = "InfoType")]
    info_type: String,
    #[serde(rename = "ComponentVerifyTicket")]
    component_verify_ticket: String,
}

/// 授权信息
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthorizationInfo {
    pub authorizer_appid: String,         // 授权方APPID
    pub authorizer_access_token: String,  // 授权方令牌
    pub expires_in: i64,                  // 有效期（秒）
    pub authorizer_refresh_token: String, // 刷新令牌，下次请使用该值获取最新令牌
    pub func_info: String,                // 功能授权
    pub end_timestamp: i64,               // 有效期截止时间（时间戳，秒）
}

/// 授权方令牌信息
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthorizationAccessToken {
    pub authorizer_access_token: String,  // 授权方令牌
    pub expires_in: i64,                  // 有效期（秒）
    pub authorizer_refresh_token: String, // 刷新令牌，下次请使用该值获取最新令牌
    pub end_timestamp: i64,               // 有效期截止时间（时间戳，秒）
}

/// 微信开放平台
#[derive(Deserialize)]
pub struct Open {
    pub wechat: Wechat,
}

impl Open { 

    /// 刷新微信公众号令牌
    /// 在 数据库中存放了各个已授权的微信公众号授权信息，通过 authorizer_appid 区分。
    /// 需要定时去获取即将过期的公众号授权信息，获取最新的公众号令牌（authorizer_refresh_token）并重新存入数据库中
    pub async fn refresh_open_authorizer(&self) -> Result<bool, MyErr> {
        if let Ok(rows) = Mdb::table("weixin_authorizer").where_add_str("status > ?", json!([0])).all::<Value>().await {
            for row in rows {
                let end_timestamp = row.get("end_timestamp").and_then(Value::as_i64).unwrap_or_default();
                if end_timestamp - 660 < util_date::now_seconds() {
                    // 如果结束时间小于当前时间，则需要刷新获取最新

                    let id = row.get("id").and_then(Value::as_i64).unwrap_or_default();
                    let authorizer_appid = row.get("authorizer_appid").and_then(Value::as_str).unwrap_or_default();
                    let authorizer_refresh_token = row.get("authorizer_refresh_token").and_then(Value::as_str).unwrap_or_default();

                    if let Ok(bean) = self.refresh_authorization_access_token(authorizer_appid, authorizer_refresh_token).await {
                        let _ = Mdb::table("weixin_authorizer").data(&bean).updae_by_id(&SystemUser {}, id, false).await;
                    } else {
                        log::error!("刷新公众号令牌失败！")
                    }
                }
            }
        } else {
            log::error!("获取公众号授权信息失败！");
        }
        Ok(true)
    }

    /// 获取 验证票据（component_verify_ticket）并存如缓存中 （mycache）
    /// component_verify_ticket 的有效时间为12小时
    pub fn save_component_verify_ticket(
        &self,
        timestamp: &str,
        nonce: &str,
        _encrypt_type: &str,
        msg_signature: &str,
        body: &str,
    ) -> Result<String, MyErr> {
        if let Ok(enc_xml) = from_str::<ComponentVerifyTicketEncXML>(&body) {
            log::info!("xml item: {:?}", enc_xml);

            if self.wechat.check_msg_signature(&msg_signature, &timestamp, &nonce, &enc_xml.encrypt) {
                // 如果校验正确，则开始解密
                let ticket_xml = self.wechat.aes_decrypt_weixin_msg(&enc_xml.encrypt)?;
                // 把解密后的数据填入 xml
                if let Ok(dec_xml) = from_str::<ComponentVerifyTicketDecXML>(&ticket_xml) {
                    // 这里设置有效期为 10小时
                    if my_cache::set(COMPONENT_VERIFY_TICKET_KEY, dec_xml.component_verify_ticket.clone(), 10 * 60 * 60) {
                        Ok(dec_xml.component_verify_ticket)
                    } else {
                        Err(MyErr::Msg(
                            "获取 component_verify_ticket 成功，但是存入缓存的时候失败了".to_string(),
                        ))
                    }
                } else {
                    Err(MyErr::Msg("解密后的ticket解析为XML错误".to_string()))
                }
            } else {
                Err(MyErr::Msg("消息合法性验证失败".to_string()))
            }
        } else {
            Err(MyErr::Msg("解析XML数据错误".to_string()))
        }
    }

    /// 获取 验证票据（component_verify_ticket）
    /// 先从缓存中获取，如果没有则从数据库中读取
    pub async fn get_component_verify_ticket(&self) -> Result<String, MyErr> {
        if let Some(component_verify_ticket) = my_cache::get(COMPONENT_VERIFY_TICKET_KEY) {
            log::info!(
                "缓存中已经存在 component_verify_ticket ，直接获取并返回: {}",
                component_verify_ticket
            );
            return Ok(component_verify_ticket);
        } else {
            let bean = Mdb::table("sys_settings")
                .where_add_str("params_key = ?", json!(["component_verify_ticket"]))
                .one_force::<Value>()
                .await?;

            let params_value = bean.get("params_value").and_then(Value::as_str).unwrap_or_default();

            // 这里设置有效期为 10小时
            my_cache::set(COMPONENT_VERIFY_TICKET_KEY, params_value.to_string(), 20 * 60);

            return Ok(params_value.to_string());
        }
    }

    /// 获取令牌(component_access_token)
    /// 如果在缓存gcache中已经存在该值，就直接返回
    /// 如果不存在，则通过接口获取最新的 component_access_token
    pub async fn get_component_access_token(&self) -> Result<String, MyErr> {
        if let Some(s) = my_cache::get(COMPONENT_VERIFY_TOKEN_KEY) {
            log::info!("缓存中已经存在 component_access_token ，直接获取并返回: {}", s);
            Ok(s)
        } else {
            if let Ok(component_verify_ticket) = self.get_component_verify_ticket().await {
                let data = json!({
                  "component_appid": self.wechat.appid,
                  "component_appsecret": self.wechat.app_secret,
                  "component_verify_ticket": component_verify_ticket,
                });

                let obj = super::post_json_to_url(GET_COMPONENT_TOKEN_URL, data).await?;
                log::info!("get_component_access_token 得到结果数据：{:?}", obj);

                if let Some(v) = obj.get("component_access_token").and_then(Value::as_str) {
                    let expires_in = obj.get("expires_in").and_then(Value::as_i64).unwrap_or(3600); // 获取有效期，单位秒
                                                                                                    // 获取到 component_access_token， 把它存入 缓存中
                    my_cache::set(COMPONENT_VERIFY_TOKEN_KEY, v.to_string(), expires_in); // 有效期2小时
                    Ok(v.to_string())
                } else {
                    Err(MyErr::Msg(format!("无法获取 component_access_token ：{:?}", obj)))
                }
            } else {
                Err(MyErr::Msg(
                    "无法获取 component_access_token ，因为却少：component_verify_ticket".to_string(),
                ))
            }
        }
    }

    ///预授权码（pre_auth_code）是第三方平台方实现授权托管的必备信息，每个预授权码有效期为 1800秒。
    /// 需要先获取令牌(component_access_token)才能调用。
    pub async fn get_preauth_code(&self) -> Result<String, MyErr> {
        if let Some(s) = my_cache::get(PRE_AUTH_CODE_KEY) {
            log::info!("缓存中已经存在 pre_auth_code ，直接获取并返回: {}", s);
            Ok(s)
        } else {
            let component_access_token = self.get_component_access_token().await?;

            let url = format!("{}{}", GET_PREAUTHCODE_URL, component_access_token);

            let data = json!({
              "component_appid": self.wechat.appid,
            });

            let obj = super::post_json_to_url(&url, data).await?;
            log::info!("get_preauth_code 得到结果数据：{:?}", obj);

            if let Some(v) = obj.get("pre_auth_code").and_then(Value::as_str) {
                let expires_in = obj.get("expires_in").and_then(Value::as_i64).unwrap_or(600); // 获取有效期，单位秒
                                                                                               // 获取到 component_access_token， 把它存入 缓存中
                my_cache::set(PRE_AUTH_CODE_KEY, v.to_string(), expires_in); // 有效期2小时
                Ok(v.to_string())
            } else {
                Err(MyErr::Msg(format!("无法获取 pre_auth_code ：{:?}", obj)))
            }
        }
    }

    /// 使用授权码(auth_code)获取授权信息(authorizer_access_token,authorizer_refresh_token)
    /// 当用户在第三方平台授权页中完成授权流程后，第三方平台开发者可以在回调 URI 中通过 URL 参数获取授权码。
    /// 使用以下接口可以换取公众号/小程序的授权信息。建议保存授权信息中的刷新令牌（authorizer_refresh_token）。
    pub async fn get_authorization_info(&self, auth_code: &str) -> Result<AuthorizationInfo, MyErr> {
        let component_access_token = self.get_component_access_token().await?;
        let url = format!("{}{}", GET_QUERY_AUTH_URL, component_access_token);

        let data = json!({
          "component_appid": self.wechat.appid,
          "authorization_code": auth_code,
        });

        let obj = super::post_json_to_url(&url, data).await?;
        log::info!("get_authorizer_access_token 得到结果数据：{:?}", obj);

        if let Some(i) = obj.get("authorization_info") {
            let expires_in = i.get("expires_in").and_then(Value::as_i64).unwrap_or_default();
            let info = AuthorizationInfo {
                authorizer_appid: i.get("authorizer_appid").and_then(Value::as_str).unwrap_or_default().to_string(),
                authorizer_access_token: i.get("authorizer_access_token").and_then(Value::as_str).unwrap_or_default().to_string(),
                expires_in: expires_in,
                authorizer_refresh_token: i.get("authorizer_refresh_token").and_then(Value::as_str).unwrap_or_default().to_string(),
                func_info: i.get("func_info").unwrap_or(&json!({})).to_string(),
                end_timestamp: util_date::now_seconds() + expires_in,
            };

            my_cache::set(
                &format!("{}{}", info.authorizer_appid.clone(), AUTHORIZER_ACCESS_TOKEN),
                info.authorizer_access_token.clone(),
                expires_in,
            );

            Ok(info)
        } else {
            Err(MyErr::Msg(format!("无法获取 authorization_info ：{:?}", obj)))
        }
    }

    /// 获取/刷新接口调用令牌
    /// 在公众号/小程序接口调用令牌（authorizer_access_token）失效时，
    /// 可以使用刷新令牌（authorizer_refresh_token）获取新的接口调用令牌。
    pub async fn refresh_authorization_access_token(
        &self,
        authorizer_appid: &str,
        authorizer_refresh_token: &str,
    ) -> Result<AuthorizationAccessToken, MyErr> {
        let component_access_token = self.get_component_access_token().await?;
        let url = format!("{}{}", GET_AUTHORIZER_URL, component_access_token);

        let data = json!({
          "component_appid": self.wechat.appid,
          "authorizer_appid": authorizer_appid,
          "authorizer_refresh_token": authorizer_refresh_token,
        });

        let obj = super::post_json_to_url(&url, data).await?;
        log::info!("refresh_authorization_access_token 得到结果数据：{:?}", obj);

        if obj.get("authorizer_access_token").is_some() {
            let expires_in = obj.get("expires_in").and_then(Value::as_i64).unwrap_or_default();
            let info = AuthorizationAccessToken {
                authorizer_access_token: obj.get("authorizer_access_token").and_then(Value::as_str).unwrap_or_default().to_string(),
                expires_in: expires_in,
                authorizer_refresh_token: obj.get("authorizer_refresh_token").and_then(Value::as_str).unwrap_or_default().to_string(),
                end_timestamp: util_date::now_seconds() + expires_in,
            };

            my_cache::set(
                &format!("{}{}", authorizer_appid, AUTHORIZER_ACCESS_TOKEN),
                info.authorizer_access_token.clone(),
                expires_in,
            );

            Ok(info)
        } else {
            Err(MyErr::Msg(format!("无法获取 authorizer_access_token ：{:?}", obj)))
        }
    }
}
